import { MotionID } from "../motion/MotionLibrary"
import { Attribute } from "./Attribute"

// 由motion击中单位派发的信息
export interface DamageInformation {
    fromSceneID:number
    targetSceneID: number
    motionName: MotionID
    frame: number
}

// 注册attackable需要的信息
export interface StageUnitElement {
    sceneID: number
    attackableGameObject: GameObject
    hitCallback: (damageMessage: DamageInformation) => void
    caller: any
}

// 计算伤害所需context
export interface DamageCalculatorContext {
    motionID: MotionID
    motionFrame: number
    from: Attribute.AttributeValueObject
    //为空则代表受击单位所有属性全部为0
    to?: Attribute.AttributeValueObject
}

export class UnitStage{
    private static stageMapping:Map<number,StageUnitElement> = new Map()
    private static temporaryTrigger:Trigger

    static get StageUnitCollection(): Map<number, StageUnitElement> {
        return this.stageMapping
    }

    // 添加单位到stage
    static addUnitToStage(stageUnitInfo: StageUnitElement) {
        if (!stageUnitInfo.attackableGameObject) {
            console.error(`添加了空的gameObject到UnitStage内,ID为:${stageUnitInfo.sceneID}`);
            return
        }
        if (this.stageMapping.has(stageUnitInfo.sceneID)) {
            console.error(`逻辑错误! 添加了重复的sceneID:${stageUnitInfo.sceneID}`);
            return
        }
        this.stageMapping.set(stageUnitInfo.sceneID, stageUnitInfo)
    }

    // 移除单位从stage
    static removeUnitFromStage(sceneID: number) {
        this.stageMapping.delete(sceneID)
    }

    // 根据sceneID获取stage内的信息
    static getStageUnit(sceneID: number): StageUnitElement | undefined {
        return this.stageMapping.get(sceneID)
    }

    // 获取给定范围内最近的可攻击单位
    static getNearAttackableStageUnit(sourcePosition: Vector, clamp: number): number | undefined {
        let result: number
        let minDis = 1e8
        for (const info of this.stageMapping.values()) {
            if (!info.attackableGameObject.getVisibility()) continue
            let dis = Vector.distance(info.attackableGameObject.worldTransform.position, sourcePosition)
            if (dis < clamp && dis < minDis) {
                result = info.sceneID
                minDis = dis
            }
        }
        return result
    }

    // 调用已注册的单位受击回调
    static invokeRegisteredProcessFunction(fromSceneID:number, sceneID: number, motionName: MotionID, frame: number) {
        let stageUnitInfo = this.getStageUnit(sceneID)
        if (!stageUnitInfo) return

        let message: DamageInformation = { fromSceneID: fromSceneID, targetSceneID: sceneID, motionName: motionName, frame: frame }
        stageUnitInfo.hitCallback.call(stageUnitInfo.caller, message)
    }

    // 测试所有stage内的单位是否在给定判定范围之内
    static testAllStageUnitInArea(trs: number[], from: GameObject, filter?: (sceneID: number) => boolean): number[] | undefined {
        let trigger = this.getTemporaryTrigger()
        trigger.parent = from
        trigger.localTransform.position = new Vector(trs[0], trs[1], trs[2])
        trigger.localTransform.rotation = new Rotation(trs[3], trs[4], trs[5])
        trigger.worldTransform.scale = new Vector(trs[6], trs[7], trs[8])

        let sourceLocation = trigger.worldTransform.position
        let result: number[]
        for (let unitInfo of this.stageMapping.values()) {
            // todo 600常量这个琢磨琢磨怎么优化
            if (Vector.distance(unitInfo.attackableGameObject.worldTransform.position, sourceLocation) > 1500) continue

            if (filter && filter(unitInfo.sceneID) == true) continue

            if (!trigger.checkInArea(unitInfo.attackableGameObject)) continue

            if (!result) result = []
            result.push(unitInfo.sceneID)
        }
        this.returnTrigger(trigger)
        return result
    }

    // 返回所有碰撞到的玩家ID
    static testAllPlayerInArea(trs: number[], from:  Vector, dir: Vector, filter?: (playerID: number) => boolean): number[] | undefined {
        let trigger = this.getTemporaryTrigger()

        let forward = dir
        let up = Vector.up
        let right = Vector.cross(forward, up)

        trigger.worldTransform.position = this.local2WorldPos(from, forward, right, trs)
        trigger.worldTransform.scale = new Vector(trs[6], trs[7], trs[8])
        trigger.worldTransform.rotation = dir.toRotation()

        let result: number[]
        for (let player of Player.getAllPlayers()) {
            let playerID = player.playerId

            if (filter && filter(playerID) == true) continue

            if (!trigger.checkInArea(player.character)) continue

            result = result || []
            result.push(playerID)
        }

        this.returnTrigger(trigger)
        return result
    }

    // 从对象池获取Trigger
    private static getTemporaryTrigger(): Trigger {
        if (!this.temporaryTrigger) {
            this.temporaryTrigger = GameObject.spawn("Trigger", {replicates: true }) as Trigger
        }
        this.temporaryTrigger.setCollision(CollisionStatus.QueryOnly)
        return this.temporaryTrigger
    }

    // 返回触发器
    private static returnTrigger(trigger: Trigger) {
        trigger.setCollision(CollisionStatus.Off)
        trigger.parent = null
    }

    // 相对位置转世界位置
    private static local2WorldPos(position: Vector, forward: Vector, right: Vector, trs: number[]): Vector {
        return new Vector(
            trs[0] * forward.x + trs[1] * right.x + position.x,
            trs[0] * forward.y + trs[1] * right.y + position.y,
            trs[0] * forward.z + trs[1] * right.z + trs[2] + position.z,
        )
    }
}